//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"

//SAGITTARIUS headers:
#include "sagittarius/parallel/wrfutex.h"
#include "sagittarius/parallel/atomics.h"

#define __USE_GNU
#include <linux/futex.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <limits.h>

#include "sagittarius/io/io.h"

void wlfutex(wrfutex *f) {
    wrfutex ff;
    while(1) {
        ff = *f;
        if(!(ff&0x0F000000)) {
            if(sg_cas32(f, ff, ff|0x01000000))
                break; //successfully acquired writer lock
            continue; //try again
        }
        //another writer already holds the lock
        //wait
        if(sg_cas32((uint32*)f, ff, ff = ff|0x10000000))
            syscall(SYS_futex, f, FUTEX_WAIT, ff, NULL);
    }
    //wait for readers to finish
    ff = *f;
    while(ff & 0x7FFFFF) {
        syscall(SYS_futex, f, FUTEX_WAIT, ff, NULL);
        ff = *f;
    }
}

void wufutex(volatile wrfutex *f) {
    wrfutex ff = *f;
    AZ(ff & 0x7FFFFF); //Invalid state
    *f = 0x00000000;
    if(ff & 0xFF800000)
        syscall(SYS_futex, f, FUTEX_WAKE, INT_MAX);
}

void rlfutex(wrfutex *f) {
    wrfutex ff;
    while(1) {
        ff = *f;
        if(ff&0xFF000000) {
            //writer lock
            //indicate that there are readers waiting
            if(sg_cas32((uint32*)f, ff, ff = ff|0x800000))
                syscall(SYS_futex, f, FUTEX_WAIT, ff, NULL); //wait for it
            continue;
        }
        //no writers
        AZ(ff & 0x800000); //Invalid state
        if(sg_cas32((uint32*)f, ff, ff+1))
            break; //successfully acquired reader lock
    }
}

void rufutex(wrfutex *f) {
    wrfutex ff;
    while(1) {
        ff = *f;
        if(ff & 0xFF000000) {
            //writers are waiting
            if(sg_cas32((uint32*)f, ff, ff-1)) {
                if(((ff-1) & 0x7FFFFF) == 0) { //Beware: & given lower prec than == (GCC 4.6.3)
                    //last reader to finish
                    syscall(SYS_futex, f, FUTEX_WAKE, INT_MAX); //wake writers
                }
                break;
            } else continue;
        }
        //no writers waiting
        if(sg_cas32((uint32*)f, ff, ff-1))
            break;
    }
}